fix lowranceusr4 writer issues (#376)
authortsteven4 <13596209+tsteven4@users.noreply.github.com>
Thu, 29 Aug 2019 15:38:04 +0000 (09:38 -0600)
committerGitHub <noreply@github.com>
Thu, 29 Aug 2019 15:38:04 +0000 (09:38 -0600)
* fixes for lowraneusr writer for versions >= 4.

use fs_chain_find when looking for FS_LOWRANCEUSR4 data.
and check for nullptr to verify FS_LOWRANCEUSR4 data exists.

* using rounding in lowranceusr lat/lon conversions.

This gives as a chance of usr->gpx->usr matching.

Update gpsbabel generated usr reference files for these
rounding differences.

* fix lowranceusr4 writer issues.

when writing embedded UTF-16LE strings in lowranceusr version >=4
don't inlucde BOM.

don't offset returned values from lowranceusr4_jd_from_timestamp
by 12 hours, thus matching the inverse transformation in
lowranceusr4_get_timestamp.

when writing lowranceusr version >=4 trails output the given number
of mysterious attribute bytes, i.e. 0.

This almost enables a gpx -> usr -> gpx test loop, which is included
but commented in lowranceusr.test.

* fixes to enable lowranceusr v4 round trip testing.

use templates for lowranceusr*_find_desc_from_icon_number
and lowranceusr*_find_icon_number_from_desc.

enhance lowranceusr*_find_icon_number_from_desc to
recongnize icon-n where n is an integer as icon number n.
This is necessary for the v4 round trip test.

declare lowranceusr*_find_desc_from_icon_number to be static
to limit visibilty. Drop const return type.

declare lowranceusr4_find_color_from_icon_number_plus_color_index
to be static to limit visiblity.

* fix new lowranceusr memory leak.

lowranceusr.cc
reference/lowrance-enchilada.usr
reference/lowrance-ignoreicons.usr
reference/lowrance.usr
testo.d/lowranceusr.test

index 46b3fd7afecaf62c9625d5da818c7e872faffc91..7fe9b7bd67afd59a7897ab74a5c56fef550da298 100644 (file)
@@ -97,6 +97,7 @@
 #include <QtCore/QLatin1String>  // for QLatin1String
 #include <QtCore/QString>        // for QString, operator+, operator==, operator!=
 #include <QtCore/QTextCodec>     // for QTextCodec
+#include <QtCore/QTextEncoder>   // for QTextEncoder
 #include <QtCore/QTime>          // for QTime
 #include <QtCore/Qt>             // for CaseInsensitive, UTC
 #include <QtCore/QtGlobal>       // for qPrintable, uint, foreach
@@ -557,7 +558,9 @@ lowranceusr4_writestr(const QString& buf, gbfile* file, int bytes_per_char)
   if (bytes_per_char == 1) {
     qba = buf.toUtf8();
   } else {
-    qba = utf16le_codec->fromUnicode(buf);
+    QTextEncoder* encoder = utf16le_codec->makeEncoder(QTextCodec::IgnoreHeader);
+    qba = encoder->fromUnicode(buf);
+    delete encoder;
   }
   int len = qba.size();
   gbfputint32(len, file_out);
@@ -574,18 +577,17 @@ lowranceusr4_get_timestamp(unsigned int jd_number, unsigned int msecs)
 static Lowranceusr4Timestamp
 lowranceusr4_jd_from_timestamp(gpsbabel::DateTime qdt)
 {
-  QDateTime jdt = qdt.toUTC().addSecs(-60 * 60 * 12);
+  QDateTime jdt = qdt.toUTC();
   unsigned int jd_number = jdt.date().toJulianDay();
   QTime jd_time = jdt.time();
   unsigned int msecs = (((((jd_time.hour() * 60) + jd_time.minute()) * 60) + jd_time.second()) * 1000) + jd_time.msec();
   return Lowranceusr4Timestamp(jd_number, msecs);
 }
 
-
-const QString
-lowranceusr_find_desc_from_icon_number(const int icon)
+template <typename T>
+static QString lowranceusr_common_find_desc_from_icon_number(const int icon, const T icon_value_table[])
 {
-  for (const lowranceusr_icon_mapping_t* i = lowranceusr_icon_value_table; i->icon; i++) {
+  for (const T* i = icon_value_table; i->icon; i++) {
     if (icon == i->value) {
       return i->icon;
     }
@@ -595,70 +597,58 @@ lowranceusr_find_desc_from_icon_number(const int icon)
   return QString("icon-%1").arg(icon);
 }
 
-static int
-lowranceusr_find_icon_number_from_desc(const QString& desc)
+template <typename T>
+static int lowranceusr_common_find_icon_number_from_desc(const QString& desc, const T icon_value_table[], const int def_icon)
 {
   if (desc.isNull()) {
-    return DEF_ICON;
+    return def_icon;
   }
 
   /*
    * If we were given a numeric icon number as a description
    * (i.e. 8255), just return that.
+   * Also return the icon number for descriptions of "icon-"
+   * followed by a numeric icon number.
    */
-  int n = desc.toInt();
+  int n = desc.mid(desc.startsWith("icon-") ? 5 : 0).toInt();
   if (n)  {
     return n;
   }
 
-  for (const lowranceusr_icon_mapping_t* i = lowranceusr_icon_value_table; i->icon; i++) {
+  for (const T* i = icon_value_table; i->icon; i++) {
     if (desc.compare(i->icon,Qt::CaseInsensitive) == 0) {
       return i->value;
     }
   }
 
-  return DEF_ICON;
+  return def_icon;
 }
 
-const QString
-lowranceusr4_find_desc_from_icon_number(const int icon)
+static QString
+lowranceusr_find_desc_from_icon_number(const int icon)
 {
-  for (const lowranceusr4_icon_mapping_t* i = lowranceusr4_icon_value_table; i->icon; i++) {
-    if (icon == i->value) {
-      return i->icon;
-    }
-  }
-
-  // Didn't find it in table, default to leave it as the number found
-  return QString("icon-%1").arg(icon);
+  return lowranceusr_common_find_desc_from_icon_number(icon, lowranceusr_icon_value_table);
 }
 
 static int
-lowranceusr4_find_icon_number_from_desc(const QString& desc)
+lowranceusr_find_icon_number_from_desc(const QString& desc)
 {
-  if (desc.isNull()) {
-    return DEF_USR4_ICON;
-  }
-
-  /*
-   * If we were given a numeric icon number as a description
-   * (i.e. 8255), just return that.
-   */
-  int n = desc.toInt();
-  if (n)  {
-    return n;
-  }
+  return lowranceusr_common_find_icon_number_from_desc(desc, lowranceusr_icon_value_table, DEF_ICON);
+}
 
-  for (const lowranceusr4_icon_mapping_t* i = lowranceusr4_icon_value_table; i->icon; i++) {
-    if (desc.compare(i->icon,Qt::CaseInsensitive) == 0) {
-      return i->value;
-    }
-  }
+static QString
+lowranceusr4_find_desc_from_icon_number(const int icon)
+{
+  return lowranceusr_common_find_desc_from_icon_number(icon, lowranceusr4_icon_value_table);
+}
 
-  return DEF_USR4_ICON;
+static int
+lowranceusr4_find_icon_number_from_desc(const QString& desc)
+{
+  return lowranceusr_common_find_icon_number_from_desc(desc, lowranceusr4_icon_value_table, DEF_USR4_ICON);
 }
 
-const char *
+static const char *
 lowranceusr4_find_color_from_icon_number_plus_color_index(const int icon, const int index)
 {
   for (const lowranceusr4_icon_mapping_t* i = lowranceusr4_icon_value_table; i->icon; i++) {
@@ -799,13 +789,13 @@ lat_mm_to_deg(double x)
 static long
 lon_deg_to_mm(double x)
 {
-  return (long)(x * SEMIMINOR * DEGREESTORADIANS);
+  return round(x * SEMIMINOR * DEGREESTORADIANS);
 }
 
 static long
 lat_deg_to_mm(double x)
 {
-  return (long)(SEMIMINOR * log(tan((x * DEGREESTORADIANS + M_PI / 2.0) / 2.0)));
+  return round(SEMIMINOR * log(tan((x * DEGREESTORADIANS + M_PI / 2.0) / 2.0)));
 }
 
 
@@ -1802,11 +1792,13 @@ lowranceusr_waypt_disp(const Waypoint* wpt)
 static void
 lowranceusr4_waypt_disp(const Waypoint* wpt)
 {
+  lowranceusr4_fsdata* fs = (lowranceusr4_fsdata*) fs_chain_find(wpt->fs, FS_LOWRANCEUSR4);
+
   /* UID unit number */
   if (opt_serialnum_i > 0) {
     gbfputint32(opt_serialnum_i, file_out);  // use option serial number if specified
-  } else if (wpt->fs != nullptr) {
-    gbfputint32(((lowranceusr4_fsdata*)(wpt->fs))->uid_unit, file_out);  // else use serial number from input if valid
+  } else if (fs != nullptr) {
+    gbfputint32(fs->uid_unit, file_out);  // else use serial number from input if valid
   } else {
     gbfputint32(0, file_out);  // else Write Serial Number = 0
   }
@@ -1840,8 +1832,8 @@ lowranceusr4_waypt_disp(const Waypoint* wpt)
     ColorId = 0; // default
   } else {
     SymbolId = lowranceusr4_find_icon_number_from_desc(wpt->icon_descr);
-    if (wpt->fs != nullptr) {
-      ColorId = lowranceusr4_find_index_from_icon_desc_and_color_desc(wpt->icon_descr, ((lowranceusr4_fsdata*)(wpt->fs))->color_desc);
+    if (fs != nullptr) {
+      ColorId = lowranceusr4_find_index_from_icon_desc_and_color_desc(wpt->icon_descr, fs->color_desc);
     } else {
       ColorId = DEF_USR4_COLOR; // default
     }
@@ -1869,8 +1861,8 @@ lowranceusr4_waypt_disp(const Waypoint* wpt)
   gbfputc(0, file_out);
 
   /* Depth in feet */
-  if (wpt->fs != nullptr) {
-    gbfputint32(((lowranceusr4_fsdata*)(wpt->fs))->depth, file_out);
+  if (fs != nullptr) {
+    gbfputint32(fs->depth, file_out);
   } else {
     gbfputint32(0, file_out); // zero seems to indicate no depth
   }
@@ -2054,11 +2046,13 @@ lowranceusr4_route_hdr(const route_head* rte)
            route_uid, qPrintable(rte->rte_name), rte->rte_waypt_ct);
   }
 
+  lowranceusr4_fsdata* fs = (lowranceusr4_fsdata*) fs_chain_find(rte->fs, FS_LOWRANCEUSR4);
+
   /* UID unit number */
   if (opt_serialnum_i > 0) {
     gbfputint32(opt_serialnum_i, file_out);  // use option serial number if specified
-  } else if (rte->fs != nullptr) {
-    gbfputint32(((lowranceusr4_fsdata*)(rte->fs))->uid_unit, file_out);  // else use serial number from input if valid
+  } else if (fs != nullptr) {
+    gbfputint32(fs->uid_unit, file_out);  // else use serial number from input if valid
   } else {
     gbfputint32(0, file_out);  // else Write Serial Number = 0
   }
@@ -2083,8 +2077,15 @@ lowranceusr4_route_leg_disp(const Waypoint* wpt)
   for (int i = 0; i < waypt_table_ct; i++) {
     Waypoint* cmp = waypt_table[i];
     if (cmp->shortname == wpt->shortname) {
-      lowranceusr4_fsdata* fsdata = (lowranceusr4_fsdata*)cmp->fs;
-      gbfputint32(fsdata->uid_unit, file_out);  // serial number from input if valid
+      lowranceusr4_fsdata* fs = (lowranceusr4_fsdata*) fs_chain_find(cmp->fs, FS_LOWRANCEUSR4);
+
+      if (opt_serialnum_i > 0) {
+        gbfputint32(opt_serialnum_i, file_out);  // use option serial number if specified
+      } else if (fs != nullptr) {
+        gbfputint32(fs->uid_unit, file_out);  // else use serial number from input if valid
+      } else {
+        gbfputint32(0, file_out);  // else Write Serial Number = 0
+      }
       gbfputint32(i, file_out); // Sequence Low
       gbfputint32(0, file_out); // Sequence High
       if (global_opts.debug_level > 1) {
@@ -2232,9 +2233,10 @@ lowranceusr4_trail_hdr(const route_head* trail)
 
   /* Mysterious "data count" and "data type" stuff */
   gbfputint32(0, file_out);
-  gbfputc(0, file_out);
-  gbfputc(0, file_out);
-  gbfputc(0, file_out);
+//  /* If we hadn't forced the count to zero we would need something like: */
+//  for (int i=0; i< attr_count; ++i) {
+//    gbfputc(0, file_out);
+//  }
 
   /* Trackpoint count */
   gbfputint32(trail->rte_waypt_ct, file_out);
index 3fb9c994b7c6753fafbd46400896b5f879b26ede..ee167782ec1d21fe6a94ac7096d73dcbd517eafb 100644 (file)
Binary files a/reference/lowrance-enchilada.usr and b/reference/lowrance-enchilada.usr differ
index c52c15aff21a538bf820151d1502d1219d51827e..9e6ba6ab376d3b01964aa4ef8c3322ed8c35c64b 100644 (file)
Binary files a/reference/lowrance-ignoreicons.usr and b/reference/lowrance-ignoreicons.usr differ
index 56a6ff70a1a6a370dad937f34f5402c4f6040473..39e8091b9f59420c9c57512ed5c7c9e2cc1c9935 100644 (file)
Binary files a/reference/lowrance.usr and b/reference/lowrance.usr differ
index 1625a96cd2115becb08ab8e1a27721c01e21502d..ee13bfc6d90bc8866e3be5765a57365921fc00c0 100644 (file)
@@ -1,7 +1,7 @@
 #
 # Lowrance USR
 # ============
-# 
+#
 # Binary data format, and slightly lossy because of the math to
 # convert lat/long.
 #
 #                              Trail 'Trail 1' conains 0 points, Trail 'Bull Run' contains 97 points in a
 #                              single section, Trail 'Hike' contains 198 points in a single section.
 #
-# lowrance-enchilada.usr       USR version 2 format file. 
+# lowrance-enchilada.usr       USR version 2 format file.
 #                              Used to validate that GPX file generated from lowrance-all.usr
 #                              when converted back to USR v2 matches previous executions of GPSBabel.
 #
-# lowrance-ignoreicons.usr     USR version 2 format file. 
+# lowrance-ignoreicons.usr     USR version 2 format file.
 #                              Used to validate use of ignoreicons option to strip out Event Marker Icons.
 #
 # lowrance-v2.usr              USR version 2 format file.  Generated on an unknown Lowrance unit.
@@ -30,7 +30,7 @@
 #                              Each trail is named 'Trail 1'.
 #                              The first 'Trail 1' has 2000 points boken into 10 sections with 200 points
 #                              in each section.  The second 'Trail 1' has 1258 points broken into 7 sections,
-#                              with 200 points in 6 and 58 in the last. 
+#                              with 200 points in 6 and 58 in the last.
 #
 # lowrance-v2-unicsv.txt       Comma seperated file generated from lowrance-v2.usr by GPSBabel.
 #                              Used to validate that the parsing of the USR version 2 file yields the same
@@ -68,22 +68,22 @@ rm -f ${TMPDIR}/lowrance*
 
 # Test ability to read non-Lowrance format and generate USR v2 file
 gpsbabel -i geo -f ${REFERENCE}/../geocaching.loc -o lowranceusr,wversion=2 -F ${TMPDIR}/lowrance1.usr
-bincompare ${TMPDIR}/lowrance1.usr ${REFERENCE}/lowrance.usr
+bincompare ${REFERENCE}/lowrance.usr ${TMPDIR}/lowrance1.usr
 
 # Use the file just created to see if can write back the same data
-gpsbabel -i lowranceusr -f ${TMPDIR}/lowrance1.usr -o lowranceusr,wversion=2 -F ${TMPDIR}/lowrance1.usr
-# Unfortunately precision issues cause mismatch on Lat/Long conversion, actual test validation commented out
-# bincompare ${REFERENCE}/lowrance.usr  ${TMPDIR}/lowrance1.usr
+gpsbabel -i lowranceusr -f ${TMPDIR}/lowrance1.usr -o lowranceusr,wversion=2 -F ${TMPDIR}/lowrance2.usr
+# At a minimum unknown altitude not making the round trip, actual test validation commented out
+# bincompare ${REFERENCE}/lowrance.usr ${TMPDIR}/lowrance2.usr
 
 # Test ability to generate GPX from USR v2 and then generate USR v2 using that newly created GPX file
 gpsbabel -i lowranceusr -f ${REFERENCE}/lowrance-all.usr -o gpx,elevprec=6 -F ${TMPDIR}/lowrance-enchilada.gpx
-gpsbabel -i gpx -f ${TMPDIR}/lowrance-enchilada.gpx -o lowranceusr,wversion=2 -F ${TMPDIR}/lowrance-enchilada1.usr
-bincompare ${TMPDIR}/lowrance-enchilada1.usr ${REFERENCE}/lowrance-enchilada.usr
+gpsbabel -i gpx -f ${TMPDIR}/lowrance-enchilada.gpx -o lowranceusr,wversion=2 -F ${TMPDIR}/lowrance-enchilada.usr
+bincompare ${REFERENCE}/lowrance-enchilada.usr ${TMPDIR}/lowrance-enchilada.usr
 
 # Don't convert icons as waypts
 gpsbabel -i lowranceusr,ignoreicons -f ${REFERENCE}/lowrance-all.usr -o gpx,elevprec=6 -F ${TMPDIR}/lowrance-enchilada.gpx
-gpsbabel -i gpx -f ${TMPDIR}/lowrance-enchilada.gpx -o lowranceusr,wversion=2 -F ${TMPDIR}/lowrance-enchilada1.usr
-bincompare ${TMPDIR}/lowrance-enchilada1.usr ${REFERENCE}/lowrance-ignoreicons.usr
+gpsbabel -i gpx -f ${TMPDIR}/lowrance-enchilada.gpx -o lowranceusr,wversion=2 -F ${TMPDIR}/lowrance-ignoreicons.usr
+bincompare ${REFERENCE}/lowrance-ignoreicons.usr ${TMPDIR}/lowrance-ignoreicons.usr
 
 #
 # Another variation of Lowrance.  Compare v2 and v3.  These reference
@@ -105,7 +105,7 @@ compare ${TMPDIR}/lowrance-v2-unicsv-sorted.txt ${TMPDIR}/lowrance-v3-unicsv-nod
 
 #
 # ${REFERENCE}/lowrance-hook2-v2.usr, ${REFERENCE}/lowrance-hook2-v3.usr,
-# ${REFERENCE}/lowrance-hook2-v4.usr, ${REFERENCE}/lowrance-hook2-v5.usr, ${REFERENCE}/lowrance-hook2-v6.usr 
+# ${REFERENCE}/lowrance-hook2-v4.usr, ${REFERENCE}/lowrance-hook2-v5.usr, ${REFERENCE}/lowrance-hook2-v6.usr
 # AND ${REFERENCE}/lowrance-hook2.gpx were generated on a single Lowrance Hook2 System.
 # This system has the ability to select any one of those formats for database export.
 # All files should contain the same basic data with increasing amounts of detail as you
@@ -117,6 +117,10 @@ compare ${TMPDIR}/lowrance-v2-unicsv-sorted.txt ${TMPDIR}/lowrance-v3-unicsv-nod
 gpsbabel -i lowranceusr -f ${REFERENCE}/lowrance-v4.usr -o gpx -F ${TMPDIR}/lowrance-v4.gpx
 compare ${REFERENCE}/lowrance-v4.gpx ${TMPDIR}/lowrance-v4.gpx
 
+gpsbabel -i gpx -f ${TMPDIR}/lowrance-v4.gpx -o lowranceusr,wversion=4 -F ${TMPDIR}/lowrance-v4.usr
+gpsbabel -i lowranceusr -f ${TMPDIR}/lowrance-v4.usr -o gpx -F ${TMPDIR}/lowrance-v4~usr.gpx
+compare ${TMPDIR}/lowrance-v4.gpx ${TMPDIR}/lowrance-v4~usr.gpx
+
 #
 # Perform USR version 5 test
 #         -------------